/*
 * Description: svga adapter hooking aspect.
 */

#include "aspect.h"
#include <functional>
#include <algorithm>
#include <Windows.h>
#include <thread>
#include <cstdio>
#include <atlbase.h>
#include "detours.h"

#pragma comment(lib, "d3d11")
#pragma comment(lib, "dxgi.lib")

namespace SVGA {
    pOriginCreateDeviceAndSwapchain g_createDeviceAndSwapchain = nullptr;
    pOriginSystemParametersInfo g_SystemParametersInfoW = nullptr;

    D3D_FEATURE_LEVEL g_levels[] = {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1,
    };

    UINT g_numLevel = 7; // levels数组含7个level

    static CComPtr<IDXGIAdapter> GetSoftwareAdapter()
    {
        CComPtr<IDXGIFactory1> pFactory = nullptr;
        HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory));
        if (pFactory == nullptr) {
            printf("CreateDXGIFactory1 failed %d\n", hr);
            return nullptr;
        }
        CComPtr<IDXGIAdapter> pAdapter = nullptr;
        for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; i++) {
            DXGI_ADAPTER_DESC desc;
            pAdapter->GetDesc(&desc);
            if ((desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) || desc.DeviceId == 0x4005) {
                break;
            }
        }
        return pAdapter;
    }

    HRESULT WINAPI MyCreateDeviceAndSwapChain(
        _In_opt_ IDXGIAdapter* pAdapter,
        D3D_DRIVER_TYPE driverType,
        HMODULE software,
        UINT flags,
        _In_opt_ const D3D_FEATURE_LEVEL* pFeatureLevels,
        UINT featureLevels,
        UINT sdkVersion,
        _In_opt_ const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc,
        _Out_opt_ IDXGISwapChain** ppSwapChain,
        _Out_opt_ ID3D11Device** ppDevice,
        _Out_opt_ D3D_FEATURE_LEVEL* pFeatureLevel,
        _Out_opt_ ID3D11DeviceContext** ppImmediateContext
    ) {
        CComPtr<IDXGIAdapter> basicRender = GetSoftwareAdapter();
        if (basicRender) {
            UINT videoMask = ~(1 << 11); // 移除第11位0x800：D3D11_CREATE_DEVICE_VIDEO_SUPPORT
            auto hr = g_createDeviceAndSwapchain(basicRender, D3D_DRIVER_TYPE_UNKNOWN,
                software, flags & videoMask, g_levels, g_numLevel, sdkVersion, pSwapChainDesc,
                ppSwapChain, ppDevice, pFeatureLevel, ppImmediateContext);
            return hr;
        }
        return g_createDeviceAndSwapchain(pAdapter, driverType,
            software, flags, g_levels, g_numLevel, sdkVersion, pSwapChainDesc,
            ppSwapChain, ppDevice, pFeatureLevel, ppImmediateContext);
    }

    LONG AppendAspectBasicRender()
    {
        LONG ret = DetourTransactionBegin();
        if (ret != NO_ERROR) {
            return ret;
        }
        ret = DetourUpdateThread(GetCurrentThread());
        if (ret != NO_ERROR) {
            DetourTransactionAbort();
            return ret;
        }
        g_createDeviceAndSwapchain = pOriginCreateDeviceAndSwapchain(
            DetourFindFunction("C:\\Windows\\System32\\d3d11.dll", "D3D11CreateDeviceAndSwapChain"));
        if (NULL == g_createDeviceAndSwapchain) {
            DetourTransactionAbort();
            return ERROR_INVALID_FUNCTION;
        }
        ret = DetourAttach((PVOID*)(&g_createDeviceAndSwapchain), MyCreateDeviceAndSwapChain);
        if (ret != NO_ERROR) {
            DetourTransactionAbort();
            return ret;
        }
        return DetourTransactionCommit();
    }

    static BOOL WINAPI MySystemParametersInfoW(
        _In_opt_ UINT uiAction,
        _In_opt_ UINT uiParam,
        _Inout_opt_ PVOID pvParam,
        _In_opt_ UINT fWinIni)
    {
        auto ret = g_SystemParametersInfoW(uiAction, uiParam, pvParam, fWinIni);
        if (uiAction == SPI_GETCLIENTAREAANIMATION && pvParam) {
            *(BOOL*)pvParam = TRUE;
        }
        return ret;
    }

    LONG AppendAspectAnimation()
    {
        LONG ret = DetourTransactionBegin();
        if (ret != NO_ERROR) {
            return ret;
        }
        ret = DetourUpdateThread(GetCurrentThread());
        if (ret != NO_ERROR) {
            DetourTransactionAbort();
            return ret;
        }
        g_SystemParametersInfoW =
            (pOriginSystemParametersInfo)DetourFindFunction(
                "C:\\Windows\\System32\\user32.dll",
                "SystemParametersInfoW");
        if (NULL == g_SystemParametersInfoW) {
            DetourTransactionAbort();
            return ERROR_INVALID_FUNCTION;
        }
        DetourAttach((PVOID*)&g_SystemParametersInfoW, MySystemParametersInfoW);
        if (ret != NO_ERROR) {
            DetourTransactionAbort();
            return ret;
        }
        return DetourTransactionCommit();
    }
}